
// Compiler implementation of the D programming language
// Copyright (c) 1999-2008 by Digital Mars
// All Rights Reserved
// written by Walter Bright
// http://www.digitalmars.com
// License for redistribution is by either the Artistic License
// in artistic.txt, or the GNU General Public License in gnu.txt.
// See the included readme.txt for details.

#include <stdio.h>
#include <assert.h>

#include "init.h"
#include "declaration.h"
#include "attrib.h"
#include "mtype.h"
#include "template.h"
#include "scope.h"
#include "aggregate.h"
#include "module.h"
#include "../l_neo_lexer/id.h"
#include "expression.h"
#include "hdrgen.h"

/********************************* P_Declaration ****************************/

P_Declaration::P_Declaration(Identifier *id)
    : P_Dsymbol(id)
{
    type = NULL;
    originalType = NULL;
    storage_class = STCundefined;
    protection = PROTundefined;
    linkage = LINKdefault;
}

void P_Declaration::semantic(Scope *sc)
{
}

const char *P_Declaration::kind()
{
    return "declaration";
}

unsigned P_Declaration::size(Loc loc)
{
    assert(type);
    return type->size();
}

int P_Declaration::isStaticConstructor()
{
    return FALSE;
}

int P_Declaration::isStaticDestructor()
{
    return FALSE;
}

int P_Declaration::isDelete()
{
    return FALSE;
}

int P_Declaration::isDataseg()
{
    return FALSE;
}

int P_Declaration::isCodeseg()
{
    return FALSE;
}

enum PROT P_Declaration::prot()
{
    return protection;
}

/*************************************
 * Check to see if declaration can be modified in this context (sc).
 * Issue error if not.
 */

#if V2
void P_Declaration::checkModify(Loc loc, Scope *sc, Type *t)
{
    if (sc->incontract && isParameter())
	error(loc, "cannot modify parameter '%s' in contract", oToChars());

    if (isCtorinit())
    {	// It's only modifiable if inside the right constructor
	P_Dsymbol *s = sc->func;
	while (1)
	{
	    P_FuncDeclaration *fd = NULL;
	    if (s)
		fd = s->isFuncDeclaration();
	    if (fd &&
		((fd->isCtorDeclaration() && storage_class & STCfield) ||
		 (fd->isStaticCtorDeclaration() && !(storage_class & STCfield))) &&
		fd->toParent() == toParent()
	       )
	    {
		P_VarDeclaration *v = isVarDeclaration();
		assert(v);
		v->ctorinit = 1;
		//printf("setting ctorinit\n");
	    }
	    else
	    {
		if (s)
		{   s = s->toParent2();
		    continue;
		}
		else
		{
		    const char *p = isStatic() ? "static " : "";
		    error(loc, "can only initialize %sconst %s inside %sconstructor",
			p, oToChars(), p);
		}
	    }
	    break;
	}
    }
    else
    {
	P_VarDeclaration *v = isVarDeclaration();
	if (v && v->canassign == 0)
	{
	    const char *p = NULL;
	    if (isConst())
		p = "const";
	    else if (isInvariant())
		p = "mutable";
	    else if (storage_class & STCmanifest)
		p = "enum";
	    else if (!t->isAssignable())
		p = "struct with immutable members";
	    if (p)
	    {	error(loc, "cannot modify %s", p);
		halt();
	    }
	}
    }
}
#endif


/********************************* P_TupleDeclaration ****************************/

P_TupleDeclaration::P_TupleDeclaration(Loc loc, Identifier *id, Objects *objects)
    : P_Declaration(id)
{
    this->type = NULL;
    this->objects = objects;
    this->isexp = 0;
    this->tupletype = NULL;
}

P_Dsymbol *P_TupleDeclaration::syntaxCopy(P_Dsymbol *s)
{
    assert(0);
    return NULL;
}

const char *P_TupleDeclaration::kind()
{
    return "tuple";
}

Type *P_TupleDeclaration::getType()
{
    /* If this tuple represents a type, return that type
     */

    //printf("P_TupleDeclaration::getType() %s\n", oToChars());
    if (isexp)
	return NULL;
    if (!tupletype)
    {
	/* It's only a type tuple if all the Object's are types
	 */
	for (size_t i = 0; i < objects->dim; i++)
	{   Object *o = (Object *)objects->data[i];

	    if (o->oDyncast() != DYNCAST_TYPE)
	    {
		//printf("\tnot[%d], %p, %d\n", i, o, o->oDyncast());
		return NULL;
	    }
	}

	/* We know it's a type tuple, so build the TypeTuple
	 */
	Arguments *args = new Arguments();
	args->setDim(objects->dim);
	OutBuffer buf;
	for (size_t i = 0; i < objects->dim; i++)
	{   Type *t = (Type *)objects->data[i];

	    //printf("type = %s\n", t->oToChars());
#if 0
	    buf.printf("_%s_%d", ident->oToChars(), i);
	    char *name = (char *)buf.extractData();
	    Identifier *id = new Identifier(name, LT::TOKidentifier);
	    Argument *arg = new Argument(STCin, t, id, NULL);
#else
	    Argument *arg = new Argument(0, t, NULL, NULL);
#endif
	    args->data[i] = (void *)arg;
	}

	tupletype = new TypeTuple(args);
    }

    return tupletype;
}

int P_TupleDeclaration::needThis()
{
    //printf("P_TupleDeclaration::needThis(%s)\n", oToChars());
    for (size_t i = 0; i < objects->dim; i++)
    {   Object *o = (Object *)objects->data[i];
	if (o->oDyncast() == DYNCAST_EXPRESSION)
	{   E_Expression *e = (E_Expression *)o;
	    if (e->op == LT::TOKdsymbol)
	    {	E_DsymbolExp *ve = (E_DsymbolExp *)e;
		P_Declaration *d = ve->s->isDeclaration();
		if (d && d->needThis())
		{
		    return 1;
		}
	    }
	}
    }
    return 0;
}

/********************************* P_TypedefDeclaration ****************************/

P_TypedefDeclaration::P_TypedefDeclaration(Loc loc, Identifier *id, Type *basetype, Initializer *init)
    : P_Declaration(id)
{
    this->type = new TypeTypedef(this);
    this->basetype = basetype->toBasetype();
    this->init = init;
#ifdef _DH
    this->htype = NULL;
    this->hbasetype = NULL;
#endif
    this->sem = 0;
    this->inuse = 0;
    this->loc = loc;
    this->sinit = NULL;
}

P_Dsymbol *P_TypedefDeclaration::syntaxCopy(P_Dsymbol *s)
{
    Type *basetype = this->basetype->syntaxCopy();

    Initializer *init = NULL;
    if (this->init)
	init = this->init->syntaxCopy();

    assert(!s);
    P_TypedefDeclaration *st;
    st = new P_TypedefDeclaration(loc, ident, basetype, init);
#ifdef _DH
    // Syntax copy for header file
    if (!htype)      // Don't overwrite original
    {	if (type)    // Make copy for both old and new instances
	{   htype = type->syntaxCopy();
	    st->htype = type->syntaxCopy();
	}
    }
    else            // Make copy of original for new instance
        st->htype = htype->syntaxCopy();
    if (!hbasetype)
    {	if (basetype)
	{   hbasetype = basetype->syntaxCopy();
	    st->hbasetype = basetype->syntaxCopy();
	}
    }
    else
        st->hbasetype = hbasetype->syntaxCopy();
#endif
    return st;
}

void P_TypedefDeclaration::semantic(Scope *sc)
{
    //printf("P_TypedefDeclaration::semantic(%s) sem = %d\n", oToChars(), sem);
    if (sem == 0)
    {	sem = 1;
	basetype = basetype->semantic(loc, sc);
	sem = 2;
	type = type->semantic(loc, sc);
	if (sc->parent->isFuncDeclaration() && init)
	    semantic2(sc);
	storage_class |= sc->stc & STCdeprecated;
    }
    else if (sem == 1)
    {
	error("circular definition");
    }
}

void P_TypedefDeclaration::semantic2(Scope *sc)
{
    //printf("P_TypedefDeclaration::semantic2(%s) sem = %d\n", oToChars(), sem);
    if (sem == 2)
    {	sem = 3;
	if (init)
	{
	    init = init->semantic(sc, basetype);

	    ExpInitializer *ie = init->isExpInitializer();
	    if (ie)
	    {
		if (ie->exp->type == basetype)
		    ie->exp->type = type;
	    }
	}
    }
}

const char *P_TypedefDeclaration::kind()
{
    return "typedef";
}

Type *P_TypedefDeclaration::getType()
{
    return type;
}

void P_TypedefDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("typedef ");
    basetype->toCBuffer(buf, ident, hgs);
    if (init)
    {
	buf->writestring(" = ");
	init->toCBuffer(buf, hgs);
    }
    buf->writeByte(';');
    buf->writenl();
}

/********************************* P_AliasDeclaration ****************************/

P_AliasDeclaration::P_AliasDeclaration(Loc loc, Identifier *id, Type *type)
    : P_Declaration(id)
{
    //printf("P_AliasDeclaration(id = '%s', type = %p)\n", id->oToChars(), type);
    //printf("type = '%s'\n", type->oToChars());
    this->loc = loc;
    this->type = type;
    this->aliassym = NULL;
#ifdef _DH
    this->htype = NULL;
    this->haliassym = NULL;
#endif
    this->overnext = NULL;
    this->inSemantic = 0;
    assert(type);
}

P_AliasDeclaration::P_AliasDeclaration(Loc loc, Identifier *id, P_Dsymbol *s)
    : P_Declaration(id)
{
    //printf("P_AliasDeclaration(id = '%s', s = %p)\n", id->oToChars(), s);
    assert(s != this);
    this->loc = loc;
    this->type = NULL;
    this->aliassym = s;
#ifdef _DH
    this->htype = NULL;
    this->haliassym = NULL;
#endif
    this->overnext = NULL;
    this->inSemantic = 0;
    assert(s);
}

P_Dsymbol *P_AliasDeclaration::syntaxCopy(P_Dsymbol *s)
{
    //printf("P_AliasDeclaration::syntaxCopy()\n");
    assert(!s);
    P_AliasDeclaration *sa;
    if (type)
	sa = new P_AliasDeclaration(loc, ident, type->syntaxCopy());
    else
	sa = new P_AliasDeclaration(loc, ident, aliassym->syntaxCopy(NULL));
#ifdef _DH
    // Syntax copy for header file
    if (!htype)	    // Don't overwrite original
    {	if (type)	// Make copy for both old and new instances
	{   htype = type->syntaxCopy();
	    sa->htype = type->syntaxCopy();
	}
    }
    else			// Make copy of original for new instance
	sa->htype = htype->syntaxCopy();
    if (!haliassym)
    {	if (aliassym)
	{   haliassym = aliassym->syntaxCopy(s);
	    sa->haliassym = aliassym->syntaxCopy(s);
	}
    }
    else
	sa->haliassym = haliassym->syntaxCopy(s);
#endif
    return sa;
}

void P_AliasDeclaration::semantic(Scope *sc)
{
    //printf("P_AliasDeclaration::semantic() %s\n", oToChars());
    if (aliassym)
    {
	if (aliassym->isTemplateInstance())
	    aliassym->semantic(sc);
	return;
    }
    this->inSemantic = 1;

    if (storage_class & STCconst)
	error("cannot be const");

    storage_class |= sc->stc & STCdeprecated;

    // Given:
    //	alias foo.bar.abc def;
    // it is not knowable from the syntax whether this is an alias
    // for a type or an alias for a symbol. It is up to the semantic()
    // pass to distinguish.
    // If it is a type, then type is set and getType() will return that
    // type. If it is a symbol, then aliassym is set and type is NULL -
    // toAlias() will return aliasssym.

    P_Dsymbol *s;
    Type *t;
    E_Expression *e;

    /* This section is needed because resolve() will:
     *   const x = 3;
     *   alias x y;
     * try to alias y to 3.
     */
    s = type->toDsymbol(sc);
    if (s && ((s->getType() && type->oEquals(s->getType())) || s->isEnumMember()))
	goto L2;			// it's a symbolic alias

    if (storage_class & STCref)
    {	// For 'ref' to be attached to function types, and picked
	// up by Type::resolve(), it has to go into sc.
	sc = sc->push();
	sc->stc |= STCref;
	type->resolve(loc, sc, &e, &t, &s);
	sc = sc->pop();
    }
    else
	type->resolve(loc, sc, &e, &t, &s);
    if (s)
    {
	goto L2;
    }
    else if (e)
    {
	// Try to convert E_Expression to P_Dsymbol
	s = getDsymbol(e);
	if (s)
	    goto L2;

	error("cannot alias an expression %s", e->oToChars());
	t = e->type;
    }
    else if (t)
	type = t;
    if (overnext)
	P_ScopeDsymbol::multiplyDefined(0, this, overnext);
    this->inSemantic = 0;
    return;

  L2:
    //printf("alias is a symbol %s %s\n", s->kind(), s->oToChars());
    type = NULL;
    P_VarDeclaration *v = s->isVarDeclaration();
    if (v && v->linkage == LINKdefault)
    {
	error("forward reference of %s", v->oToChars());
	s = NULL;
    }
    else
    {
	P_FuncDeclaration *f = s->toAlias()->isFuncDeclaration();
	if (f)
	{
	    if (overnext)
	    {
		P_FuncAliasDeclaration *fa = new P_FuncAliasDeclaration(f);
		if (!fa->overloadInsert(overnext))
		    P_ScopeDsymbol::multiplyDefined(0, f, overnext);
		overnext = NULL;
		s = fa;
		s->parent = sc->parent;
	    }
	}
	if (overnext)
	    P_ScopeDsymbol::multiplyDefined(0, s, overnext);
	if (s == this)
	{
	    assert(global.errors);
	    s = NULL;
	}
    }
    aliassym = s;
    this->inSemantic = 0;
}

int P_AliasDeclaration::overloadInsert(P_Dsymbol *s)
{
    /* Don't know yet what the aliased symbol is, so assume it can
     * be overloaded and check later for correctness.
     */

    //printf("P_AliasDeclaration::overloadInsert('%s')\n", s->oToChars());
    if (overnext == NULL)
    {	overnext = s;
	return TRUE;
    }
    else
    {
	return overnext->overloadInsert(s);
    }
}

const char *P_AliasDeclaration::kind()
{
    return "alias";
}

Type *P_AliasDeclaration::getType()
{
    return type;
}

P_Dsymbol *P_AliasDeclaration::toAlias()
{
    //printf("P_AliasDeclaration::toAlias('%s', this = %p, aliassym = %p, kind = '%s')\n", oToChars(), this, aliassym, aliassym ? aliassym->kind() : "");
    assert(this != aliassym);
    //static int count; if (++count == 10) *(char*)0=0;
    if (inSemantic)
    {	error("recursive alias declaration");
//	return this;
    }
    P_Dsymbol *s = aliassym ? aliassym->toAlias() : this;
    return s;
}

void P_AliasDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    buf->writestring("alias ");
#if 0 && _DH
    if (hgs->hdrgen)
    {
	if (haliassym)
	{
	    haliassym->toCBuffer(buf, hgs);
	    buf->writeByte(' ');
	    buf->writestring(ident->toChars());
	}
	else
	    htype->toCBuffer(buf, ident, hgs);
    }
    else
#endif
    {
	if (aliassym)
	{
	    aliassym->toCBuffer(buf, hgs);
	    buf->writeByte(' ');
	    buf->writestring(ident->oToChars());
	}
	else
	    type->toCBuffer(buf, ident, hgs);
    }
    buf->writeByte(';');
    buf->writenl();
}

/********************************* P_VarDeclaration ****************************/

P_VarDeclaration::P_VarDeclaration(Loc loc, Type *type, Identifier *id, Initializer *init)
    : P_Declaration(id)
{
    //printf("P_VarDeclaration('%s')\n", id->oToChars());
#ifdef DEBUG
    if (!type && !init)
    {	printf("P_VarDeclaration('%s')\n", id->oToChars());
	//*(char*)0=0;
    }
#endif
    assert(type || init);
    this->type = type;
    this->init = init;
#ifdef _DH
    this->htype = NULL;
    this->hinit = NULL;
#endif
    this->loc = loc;
    offset = 0;
    noauto = 0;
    inuse = 0;
    ctorinit = 0;
    aliassym = NULL;
    onstack = 0;
    canassign = 0;
    value = NULL;
    scope = NULL;
}

P_Dsymbol *P_VarDeclaration::syntaxCopy(P_Dsymbol *s)
{
    //printf("P_VarDeclaration::syntaxCopy(%s)\n", oToChars());

    P_VarDeclaration *sv;
    if (s)
    {	sv = (P_VarDeclaration *)s;
    }
    else
    {
	Initializer *init = NULL;
	if (this->init)
	{   init = this->init->syntaxCopy();
	    //init->isExpInitializer()->exp->print();
	    //init->isExpInitializer()->exp->dump(0);
	}

	sv = new P_VarDeclaration(loc, type ? type->syntaxCopy() : NULL, ident, init);
	sv->storage_class = storage_class;
    }
#ifdef _DH
    // Syntax copy for header file
    if (!htype)      // Don't overwrite original
    {	if (type)    // Make copy for both old and new instances
	{   htype = type->syntaxCopy();
	    sv->htype = type->syntaxCopy();
	}
    }
    else            // Make copy of original for new instance
        sv->htype = htype->syntaxCopy();
    if (!hinit)
    {	if (init)
	{   hinit = init->syntaxCopy();
	    sv->hinit = init->syntaxCopy();
	}
    }
    else
        sv->hinit = hinit->syntaxCopy();
#endif
    return sv;
}

void P_VarDeclaration::semantic(Scope *sc)
{
#if 0
    printf("P_VarDeclaration::semantic('%s', parent = '%s')\n", toChars(), sc->parent->toChars());
    printf(" type = %s\n", type ? type->toChars() : "null");
    printf(" stc = x%x\n", sc->stc);
    printf(" storage_class = x%x\n", storage_class);
    printf("linkage = %d\n", sc->linkage);
    //if (strcmp(toChars(), "mul") == 0) halt();
#endif

    storage_class |= sc->stc;
    if (storage_class & STCextern && init)
	error("extern symbols cannot have initializers");

    /* If auto type inference, do the inference
     */
    int inferred = 0;
    if (!type)
    {	inuse++;
	type = init->inferType(sc);
	inuse--;
	inferred = 1;

	/* This is a kludge to support the existing syntax for RAII
	 * declarations.
	 */
	storage_class &= ~STCauto;
	originalType = type;
    }
    else
    {	if (!originalType)
	    originalType = type;
	type = type->semantic(loc, sc);
    }
    //printf(" semantic type = %s\n", type ? type->oToChars() : "null");

    type->checkDeprecated(loc, sc);
    linkage = sc->linkage;
    this->parent = sc->parent;
    //printf("this = %p, parent = %p, '%s'\n", this, parent, parent->oToChars());
    protection = sc->protection;
    //printf("sc->stc = %x\n", sc->stc);
    //printf("storage_class = x%x\n", storage_class);

    P_Dsymbol *parent = toParent();
    P_FuncDeclaration *fd = parent->isFuncDeclaration();

    Type *tb = type->toBasetype();
    if (tb->ty == Tvoid && !(storage_class & STClazy))
    {	error("voids have no value");
	type = Type::terror;
	tb = type;
    }
    if (tb->ty == Tfunction)
    {	error("cannot be declared to be a function");
	type = Type::terror;
	tb = type;
    }
    if (tb->ty == Tstruct)
    {	TypeStruct *ts = (TypeStruct *)tb;

	if (!ts->sym->members)
	{
	    error("no definition of struct %s", ts->oToChars());
	}
    }

    if (tb->ty == Ttuple)
    {   /* Instead, declare variables for each of the tuple elements
	 * and add those.
	 */
	TypeTuple *tt = (TypeTuple *)tb;
	size_t nelems = Argument::dim(tt->arguments);
	Objects *exps = new Objects();
	exps->setDim(nelems);
	E_Expression *ie = init ? init->toExpression() : NULL;

	for (size_t i = 0; i < nelems; i++)
	{   Argument *arg = Argument::getNth(tt->arguments, i);

	    OutBuffer buf;
	    buf.printf("_%s_field_%zu", ident->oToChars(), i);
	    buf.writeByte(0);
	    char *name = (char *)buf.extractData();
	    Identifier *id = new Identifier(name, LT::TOKidentifier);

	    E_Expression *einit = ie;
	    if (ie && ie->op == LT::TOKtuple)
	    {	einit = (E_Expression *)((E_TupleExp *)ie)->exps->data[i];
	    }
	    Initializer *ti = init;
	    if (einit)
	    {	ti = new ExpInitializer(einit->loc, einit);
	    }

	    P_VarDeclaration *v = new P_VarDeclaration(loc, arg->type, id, ti);
	    //printf("declaring field %s of type %s\n", v->oToChars(), v->type->oToChars());
	    v->semantic(sc);

	    if (sc->scopesym)
	    {	//printf("adding %s to %s\n", v->oToChars(), sc->scopesym->oToChars());
		if (sc->scopesym->members)
		    sc->scopesym->members->push(v);
	    }

	    E_Expression *e = new E_DsymbolExp(loc, v);
	    exps->data[i] = e;
	}
	P_TupleDeclaration *v2 = new P_TupleDeclaration(loc, ident, exps);
	v2->isexp = 1;
	aliassym = v2;
	return;
    }

Lagain:
    /* Storage class can modify the type
     */
    type = type->addStorageClass(storage_class);

    /* Adjust storage class to reflect type
     */
    if (type->isConst())
    {	storage_class |= STCconst;
	if (type->isShared())
	    storage_class |= STCshared;
    }
    else if (type->isInvariant())
	storage_class |= STCinvariant;
    else if (type->isShared())
	storage_class |= STCshared;

    if (isSynchronized())
    {
	error("variable %s cannot be synchronized", oToChars());
    }
    else if (isOverride())
    {
	error("override cannot be applied to variable");
    }
    else if (isAbstract())
    {
	error("abstract cannot be applied to variable");
    }
    else if (storage_class & STCfinal)
    {
	error("final cannot be applied to variable");
    }

    if (storage_class & (STCstatic | STCextern | STCmanifest | STCtemplateparameter | STCtls))
    {
    }
    else
    {
	P_AggregateDeclaration *aad = sc->anonAgg;
	if (!aad)
	    aad = parent->isAggregateDeclaration();
	if (aad)
	{   assert(!(storage_class & (STCextern | STCstatic | STCtls)));

	    if (storage_class & (STCconst | STCinvariant) && init)
	    {
		if (!type->toBasetype()->isTypeBasic())
		    storage_class |= STCstatic;
	    }
	    else
		aad->addField(sc, this);
	}

	P_InterfaceDeclaration *id = parent->isInterfaceDeclaration();
	if (id)
	{
	    error("field not allowed in interface");
	}

	/* Templates cannot add fields to aggregates
	 */
	P_TemplateInstance *ti = parent->isTemplateInstance();
	if (ti)
	{
	    // Take care of nested templates
	    while (1)
	    {
		P_TemplateInstance *ti2 = ti->tempdecl->parent->isTemplateInstance();
		if (!ti2)
		    break;
		ti = ti2;
	    }

	    // If it's a member template
	    P_AggregateDeclaration *ad = ti->tempdecl->isMember();
	    if (ad && storage_class != STCundefined)
	    {
		error("cannot use template to add field to aggregate '%s'", ad->oToChars());
	    }
	}
    }

    if ((storage_class & (STCref | STCparameter | STCforeach)) == STCref &&
	ident != Id::This)
    {
	error("only parameters or foreach declarations can be ref");
    }

    if (type->isauto() && !noauto)
    {
	if (storage_class & (STCfield | STCout | STCref | STCstatic | STCmanifest | STCtls) || !fd)
	{
	    error("globals, statics, fields, manifest constants, ref and out parameters cannot be scope");
	}

	if (!(storage_class & (STCauto | STCscope)))
	{
	    if (!(storage_class & STCparameter) && ident != Id::withSym)
		error("reference to scope class must be scope");
	}
    }

    if ((isConst() || isInvariant()) && !init && !fd)
    {	// Initialize by constructor only
	storage_class |= STCctorinit;
    }

    if (init)
	storage_class |= STCinit;     // remember we had an explicit initializer
    else if (storage_class & STCmanifest)
	error("manifest constants must have initializers");

    LT::T op = LT::TOKconstruct;
    if (!init && !sc->inunion && !isStatic() && fd &&
	(!(storage_class & (STCfield | STCin | STCforeach | STCparameter)) || (storage_class & STCout)) &&
	type->size() != 0)
    {
	// Provide a default initializer
	//printf("Providing default initializer for '%s'\n", oToChars());
	if (type->ty == Tstruct &&
	    ((TypeStruct *)type)->sym->zeroInit == 1)
	{   /* If a struct is all zeros, as a special case
	     * set it's initializer to the integer 0.
	     * In E_AssignExp::toElem(), we check for this and issue
	     * a memset() to initialize the struct.
	     * Must do same check in interpreter.
	     */
	    E_Expression *e = new E_IntegerExp(loc, 0, Type::tint32);
	    E_Expression *e1;
	    e1 = new E_VarExp(loc, this);
	    e = new E_AssignExp(loc, e1, e);
	    e->op = LT::TOKconstruct;
	    e->type = e1->type;		// don't type check this, it would fail
	    init = new ExpInitializer(loc, e);
	    return;
	}
	else if (type->ty == Ttypedef)
	{   TypeTypedef *td = (TypeTypedef *)type;
	    if (td->sym->init)
	    {	init = td->sym->init;
		ExpInitializer *ie = init->isExpInitializer();
		if (ie)
		    // Make copy so we can modify it
		    init = new ExpInitializer(ie->loc, ie->exp);
	    }
	    else
		init = getExpInitializer();
	}
	else
	{
	    init = getExpInitializer();
	}
	// Default initializer is always a blit
	op = LT::TOKblit;
    }

    if (init)
    {
	sc = sc->push();
	sc->stc &= ~(STCconst | STCinvariant | STCpure | STCnothrow | STCref | STCshared);

	ArrayInitializer *ai = init->isArrayInitializer();
	if (ai && tb->ty == Taarray)
	{
	    init = ai->toAssocArrayInitializer();
	}

	StructInitializer *si = init->isStructInitializer();
	ExpInitializer *ei = init->isExpInitializer();

	// See if initializer is a E_NewExp that can be allocated on the stack
	if (ei && isScope() && ei->exp->op == LT::TOKnew)
	{   E_NewExp *ne = (E_NewExp *)ei->exp;
	    if (!(ne->newargs && ne->newargs->dim))
	    {	ne->onstack = 1;
		onstack = 1;
		if (type->isBaseOf(ne->newtype->semantic(loc, sc), NULL))
		    onstack = 2;
	    }
	}

	// If inside function, there is no semantic3() call
	if (sc->func)
	{
	    // If local variable, use E_AssignExp to handle all the various
	    // possibilities.
	    if (fd && !isStatic() && !(storage_class & STCmanifest) &&
		!init->isVoidInitializer())
	    {
		//printf("fd = '%s', var = '%s'\n", fd->oToChars(), oToChars());
		if (!ei)
		{
		    E_Expression *e = init->toExpression();
		    if (!e)
		    {
			init = init->semantic(sc, type);
			e = init->toExpression();
			if (!e)
			{   error("is not a static and cannot have static initializer");
			    return;
			}
		    }
		    ei = new ExpInitializer(init->loc, e);
		    init = ei;
		}

		E_Expression *e1 = new E_VarExp(loc, this);

		Type *t = type->toBasetype();
		if (t->ty == Tsarray)
		{
		    ei->exp = ei->exp->semantic(sc);
		    if (!ei->exp->implicitConvTo(type))
		    {
			int dim = ((TypeSArray *)t)->dim->toInteger();
			// If multidimensional static array, treat as one large array
			while (1)
			{
			    t = t->nextOf()->toBasetype();
			    if (t->ty != Tsarray)
				break;
			    dim *= ((TypeSArray *)t)->dim->toInteger();
			    e1->type = new TypeSArray(t->nextOf(), new E_IntegerExp(0, dim, Type::tindex));
			}
		    }
		    e1 = new E_SliceExp(loc, e1, NULL, NULL);
		}
		else if (t->ty == Tstruct)
		{
		    ei->exp = ei->exp->semantic(sc);

		    /* Look to see if initializer is a call to the constructor
		     */
		    P_StructDeclaration *sd = ((TypeStruct *)t)->sym;
		    if (sd->ctor &&		// there are constructors
			ei->exp->type->ty == Tstruct &&	// rvalue is the same struct
			((TypeStruct *)ei->exp->type)->sym == sd &&
			ei->exp->op == LT::TOKstar)
		    {
			/* Look for form of constructor call which is:
			 *    *__ctmp.ctor(arguments...)
			 */
			E_PtrExp *pe = (E_PtrExp *)ei->exp;
			if (pe->e1->op == LT::TOKcall)
			{   E_CallExp *ce = (E_CallExp *)pe->e1;
			    if (ce->e1->op == LT::TOKdotvar)
			    {	E_DotVarExp *dve = (E_DotVarExp *)ce->e1;
				if (dve->var->isCtorDeclaration())
				{   /* It's a constructor call, currently constructing
				     * a temporary __ctmp.
				     */
				    /* Before calling the constructor, initialize
				     * variable with a bit copy of the default
				     * initializer
				     */
				    E_Expression *e = new E_AssignExp(loc, new E_VarExp(loc, this), t->defaultInit(loc));
				    e->op = LT::TOKblit;
				    e->type = t;
				    ei->exp = new E_CommaExp(loc, e, ei->exp);

				    /* Replace __ctmp being constructed with e1
				     */
				    dve->e1 = e1;
				    return;
				}
			    }
			}
		    }

		    if (!ei->exp->implicitConvTo(type))
		    {	Type *ti = ei->exp->type->toBasetype();
			// Don't cast away invariant or mutability in initializer
			if (!(ti->ty == Tstruct && t->toDsymbol(sc) == ti->toDsymbol(sc)))
			    ei->exp = new E_CastExp(loc, ei->exp, type);
		    }
		}
		ei->exp = new E_AssignExp(loc, e1, ei->exp);
		ei->exp->op = op;
		canassign++;
		ei->exp = ei->exp->semantic(sc);
		canassign--;
		ei->exp->optimize(WANTvalue);
	    }
	    else
	    {
		init = init->semantic(sc, type);
	    }
	}
	else if (storage_class & (STCconst | STCinvariant | STCmanifest) ||
		 type->isConst() || type->isInvariant())
	{
	    /* Because we may need the results of a const declaration in a
	     * subsequent type, such as an array dimension, before semantic2()
	     * gets ordinarily run, try to run semantic2() now.
	     * Ignore failure.
	     */

	    if (!global.errors && !inferred)
	    {
		unsigned errors = global.errors;
		global.gag++;
		//printf("+gag\n");
		E_Expression *e;
		Initializer *i2 = init;
		inuse++;
		if (ei)
		{
		    e = ei->exp->syntaxCopy();
		    e = e->semantic(sc);
		    e = e->implicitCastTo(sc, type);
		}
		else if (si || ai)
		{   i2 = init->syntaxCopy();
		    i2 = i2->semantic(sc, type);
		}
		inuse--;
		global.gag--;
		//printf("-gag\n");
		if (errors != global.errors)	// if errors happened
		{
		    if (global.gag == 0)
			global.errors = errors;	// act as if nothing happened

		    /* Save scope for later use, to try again
		     */
		    scope = new Scope(*sc);
		    scope->setNoFree();
		}
		else if (ei)
		{
		    if (isDataseg())
			/* static const/invariant does CTFE
			 */
			e = e->optimize(WANTvalue | WANTinterpret);
		    else
			e = e->optimize(WANTvalue);
		    if (e->op == LT::TOKint64 || e->op == LT::TOKstring)
		    {
			ei->exp = e;		// no errors, keep result
		    }
		    else
		    {
			/* Save scope for later use, to try again
			 */
			scope = new Scope(*sc);
			scope->setNoFree();
		    }
		}
		else
		    init = i2;		// no errors, keep result
	    }
	}
	sc = sc->pop();
    }
}

void P_VarDeclaration::semantic2(Scope *sc)
{
    //printf("P_VarDeclaration::semantic2('%s')\n", oToChars());
    if (init && !toParent()->isFuncDeclaration())
    {	inuse++;
#if 0
	ExpInitializer *ei = init->isExpInitializer();
	if (ei)
	{
	    ei->exp->dump(0);
	    printf("type = %p\n", ei->exp->type);
	}
#endif
	init = init->semantic(sc, type);
	inuse--;
    }
}

const char *P_VarDeclaration::kind()
{
    return "variable";
}

P_Dsymbol *P_VarDeclaration::toAlias()
{
    //printf("P_VarDeclaration::toAlias('%s', this = %p, aliassym = %p)\n", oToChars(), this, aliassym);
    assert(this != aliassym);
    P_Dsymbol *s = aliassym ? aliassym->toAlias() : this;
    return s;
}

void P_VarDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
{
    if (storage_class & STCconst)
	buf->writestring("const ");
    if (storage_class & STCstatic)
	buf->writestring("static ");
    if (storage_class & STCauto)
	buf->writestring("auto ");
#if V2
    if (storage_class & STCmanifest)
	buf->writestring("enum ");
    if (storage_class & STCinvariant)
	buf->writestring("immutable ");
    if (storage_class & STCshared)
	buf->writestring("shared ");
    if (storage_class & STCtls)
	buf->writestring("__thread ");
#endif

    if (type)
	type->toCBuffer(buf, ident, hgs);
    else
	buf->writestring(ident->oToChars());
    if (init)
    {	buf->writestring(" = ");
	ExpInitializer *ie = init->isExpInitializer();
	if (ie && (ie->exp->op == LT::TOKconstruct || ie->exp->op == LT::TOKblit))
	    ((E_AssignExp *)ie->exp)->e2->toCBuffer(buf, hgs);
	else
	    init->toCBuffer(buf, hgs);
    }
    buf->writeByte(';');
    buf->writenl();
}

int P_VarDeclaration::needThis()
{
    //printf("P_VarDeclaration::needThis(%s, x%x)\n", oToChars(), storage_class);
    return storage_class & STCfield;
}

int P_VarDeclaration::isImportedSymbol()
{
    if (protection == PROTexport && !init &&
	(storage_class & STCstatic || parent->isModule()))
	return TRUE;
    return FALSE;
}

void P_VarDeclaration::checkCtorConstInit()
{
#if 0 /* doesn't work if more than one static ctor */
    if (ctorinit == 0 && isCtorinit() && !(storage_class & STCfield))
	error("missing initializer in static constructor for const variable");
#endif
}

/************************************
 * Check to see if this variable is actually in an enclosing function
 * rather than the current one.
 */

void P_VarDeclaration::checkNestedReference(Scope *sc, Loc loc)
{
    if (parent && !isDataseg() && parent != sc->parent &&
	!(storage_class & STCmanifest))
    {
	// The function that this variable is in
	P_FuncDeclaration *fdv = toParent()->isFuncDeclaration();
	// The current function
	P_FuncDeclaration *fdthis = sc->parent->isFuncDeclaration();

	if (fdv && fdthis && fdv != fdthis)
	{
	    if (loc.filename)
		fdthis->getLevel(loc, fdv);

	    for (int i = 0; i < nestedrefs.dim; i++)
	    {	P_FuncDeclaration *f = (P_FuncDeclaration *)nestedrefs.data[i];
		if (f == fdthis)
		    goto L1;
	    }
	    nestedrefs.push(fdthis);
	  L1: ;


	    for (int i = 0; i < fdv->closureVars.dim; i++)
	    {	P_Dsymbol *s = (P_Dsymbol *)fdv->closureVars.data[i];
		if (s == this)
		    goto L2;
	    }

	    fdv->closureVars.push(this);
	  L2: ;

	    //printf("var %s in function %s is nested ref\n", oToChars(), fdv->oToChars());
	}
    }
}

/****************************
 * Get ExpInitializer for a variable, if there is one.
 */

ExpInitializer *P_VarDeclaration::getExpInitializer()
{
    ExpInitializer *ei;

    if (init)
	ei = init->isExpInitializer();
    else
    {
	E_Expression *e = type->defaultInit(loc);
	if (e)
	    ei = new ExpInitializer(loc, e);
	else
	    ei = NULL;
    }
    return ei;
}

/*******************************************
 * If variable has a constant expression initializer, get it.
 * Otherwise, return NULL.
 */

E_Expression *P_VarDeclaration::getConstInitializer()
{
    if ((isConst() || isInvariant() || storage_class & STCmanifest) &&
	storage_class & STCinit)
    {
	ExpInitializer *ei = getExpInitializer();
	if (ei)
	    return ei->exp;
    }

    return NULL;
}

/*************************************
 * Return !=0 if we can take the address of this variable.
 */

int P_VarDeclaration::canTakeAddressOf()
{
#if 0
    /* Global variables and struct/class fields of the form:
     *	const int x = 3;
     * are not stored and hence cannot have their address taken.
     */
    if ((isConst() || isInvariant()) &&
	storage_class & STCinit &&
	(!(storage_class & (STCstatic | STCextern)) || (storage_class & STCfield)) &&
	(!parent || toParent()->isModule() || toParent()->isTemplateInstance()) &&
	type->toBasetype()->isTypeBasic()
       )
    {
	return 0;
    }
#else
    if (storage_class & STCmanifest)
	return 0;
#endif
    return 1;
}

/*******************************
 * Does symbol go into data segment?
 * Includes extern variables.
 */

int P_VarDeclaration::isDataseg()
{
#if 0
    printf("P_VarDeclaration::isDataseg(%p, '%s')\n", this, toChars());
    printf("%x, %p, %p\n", storage_class & (STCstatic | STCconst), parent->isModule(), parent->isTemplateInstance());
    printf("parent = '%s'\n", parent->toChars());
#endif
    if (storage_class & STCmanifest)
	return 0;
    P_Dsymbol *parent = this->toParent();
    if (!parent && !(storage_class & STCstatic))
    {	error("forward referenced");
	type = Type::terror;
	return 0;
    }
    return canTakeAddressOf() &&
	(storage_class & (STCstatic | STCextern | STCtls) ||
	 toParent()->isModule() ||
	 toParent()->isTemplateInstance());
}

int P_VarDeclaration::hasPointers()
{
    //printf("P_VarDeclaration::hasPointers() %s, ty = %d\n", oToChars(), type->ty);
    return (!isDataseg() && type->hasPointers());
}

/******************************************
 * Return TRUE if variable needs to call the destructor.
 */

int P_VarDeclaration::needsAutoDtor()
{
    //printf("P_VarDeclaration::needsAutoDtor() %s\n", oToChars());

    if (noauto || storage_class & STCnodtor)
	return FALSE;

    // Destructors for structs and arrays of structs
    Type *tv = type->toBasetype();
    while (tv->ty == Tsarray)
    {   TypeSArray *ta = (TypeSArray *)tv;
	tv = tv->nextOf()->toBasetype();
    }
    if (tv->ty == Tstruct)
    {   TypeStruct *ts = (TypeStruct *)tv;
	P_StructDeclaration *sd = ts->sym;
	if (sd->dtor)
	    return TRUE;
    }

    // Destructors for classes
    if (storage_class & (STCauto | STCscope))
    {
	if (type->isClassHandle())
	    return TRUE;
    }
    return FALSE;
}


/******************************************
 * If a variable has an auto destructor call, return call for it.
 * Otherwise, return NULL.
 */

E_Expression *P_VarDeclaration::callAutoDtor(Scope *sc)
{   E_Expression *e = NULL;

    //printf("P_VarDeclaration::callAutoDtor() %s\n", oToChars());

    if (noauto || storage_class & STCnodtor)
	return NULL;

    // Destructors for structs and arrays of structs
    bool array = false;
    Type *tv = type->toBasetype();
    while (tv->ty == Tsarray)
    {   TypeSArray *ta = (TypeSArray *)tv;
	array = true;
	tv = tv->nextOf()->toBasetype();
    }
    if (tv->ty == Tstruct)
    {   TypeStruct *ts = (TypeStruct *)tv;
	P_StructDeclaration *sd = ts->sym;
	if (sd->dtor)
	{
	    if (array)
	    {
		// Typeinfo.destroy(cast(void*)&v);
		E_Expression *ea = new E_SymOffExp(loc, this, 0, 0);
		ea = new E_CastExp(loc, ea, Type::tvoid->pointerTo());
		Expressions *args = new Expressions();
		args->push(ea);

		E_Expression *et = type->getTypeInfo(sc);
		et = new E_DotIdExp(loc, et, Id::destroy);

		e = new E_CallExp(loc, et, args);
	    }
	    else
	    {
		e = new E_VarExp(loc, this);
		e = new E_DotVarExp(loc, e, sd->dtor, 0);
		e = new E_CallExp(loc, e);
	    }
	    return e;
	}
    }

    // Destructors for classes
    if (storage_class & (STCauto | STCscope))
    {
	for (P_ClassDeclaration *cd = type->isClassHandle();
	     cd;
	     cd = cd->baseClass)
	{
	    /* We can do better if there's a way with onstack
	     * classes to determine if there's no way the monitor
	     * could be set.
	     */
	    //if (cd->isInterfaceDeclaration())
		//error("interface %s cannot be scope", cd->oToChars());
	    if (1 || onstack || cd->dtors.dim)	// if any destructors
	    {
		// delete this;
		E_Expression *ec;

		ec = new E_VarExp(loc, this);
		e = new E_DeleteExp(loc, ec);
		e->type = Type::tvoid;
		break;
	    }
	}
    }
    return e;
}


/********************************* P_ClassInfoDeclaration ****************************/

P_ClassInfoDeclaration::P_ClassInfoDeclaration(P_ClassDeclaration *cd)
    : P_VarDeclaration(0, P_ClassDeclaration::classinfo->type, cd->ident, NULL)
{
    this->cd = cd;
    storage_class = STCstatic;
}

P_Dsymbol *P_ClassInfoDeclaration::syntaxCopy(P_Dsymbol *s)
{
    assert(0);		// should never be produced by syntax
    return NULL;
}

void P_ClassInfoDeclaration::semantic(Scope *sc)
{
}

/********************************* P_ModuleInfoDeclaration ****************************/

P_ModuleInfoDeclaration::P_ModuleInfoDeclaration(P_Module *mod)
    : P_VarDeclaration(0, P_Module::moduleinfo->type, mod->ident, NULL)
{
    this->mod = mod;
    storage_class = STCstatic;
}

P_Dsymbol *P_ModuleInfoDeclaration::syntaxCopy(P_Dsymbol *s)
{
    assert(0);		// should never be produced by syntax
    return NULL;
}

void P_ModuleInfoDeclaration::semantic(Scope *sc)
{
}

/********************************* P_TypeInfoDeclaration ****************************/

P_TypeInfoDeclaration::P_TypeInfoDeclaration(Type *tinfo, int internal)
    : P_VarDeclaration(0, Type::typeinfo->type, tinfo->getTypeInfoIdent(internal), NULL)
{
    this->tinfo = tinfo;
    storage_class = STCstatic;
    protection = PROTpublic;
    linkage = LINKc;
}

P_Dsymbol *P_TypeInfoDeclaration::syntaxCopy(P_Dsymbol *s)
{
    assert(0);		// should never be produced by syntax
    return NULL;
}

void P_TypeInfoDeclaration::semantic(Scope *sc)
{
    assert(linkage == LINKc);
}

/***************************** P_TypeInfoConstDeclaration **********************/

#if V2
P_TypeInfoConstDeclaration::P_TypeInfoConstDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}
#endif

/***************************** P_TypeInfoInvariantDeclaration **********************/

#if V2
P_TypeInfoInvariantDeclaration::P_TypeInfoInvariantDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}
#endif

/***************************** P_TypeInfoSharedDeclaration **********************/

#if V2
P_TypeInfoSharedDeclaration::P_TypeInfoSharedDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}
#endif

/***************************** P_TypeInfoStructDeclaration **********************/

P_TypeInfoStructDeclaration::P_TypeInfoStructDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}

/***************************** P_TypeInfoClassDeclaration ***********************/

P_TypeInfoClassDeclaration::P_TypeInfoClassDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}

/***************************** P_TypeInfoInterfaceDeclaration *******************/

P_TypeInfoInterfaceDeclaration::P_TypeInfoInterfaceDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}

/***************************** P_TypeInfoTypedefDeclaration *********************/

P_TypeInfoTypedefDeclaration::P_TypeInfoTypedefDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}

/***************************** P_TypeInfoPointerDeclaration *********************/

P_TypeInfoPointerDeclaration::P_TypeInfoPointerDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}

/***************************** P_TypeInfoArrayDeclaration ***********************/

P_TypeInfoArrayDeclaration::P_TypeInfoArrayDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}

/***************************** P_TypeInfoStaticArrayDeclaration *****************/

P_TypeInfoStaticArrayDeclaration::P_TypeInfoStaticArrayDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}

/***************************** P_TypeInfoAssociativeArrayDeclaration ************/

P_TypeInfoAssociativeArrayDeclaration::P_TypeInfoAssociativeArrayDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}

/***************************** P_TypeInfoEnumDeclaration ***********************/

P_TypeInfoEnumDeclaration::P_TypeInfoEnumDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}

/***************************** P_TypeInfoFunctionDeclaration ********************/

P_TypeInfoFunctionDeclaration::P_TypeInfoFunctionDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}

/***************************** P_TypeInfoDelegateDeclaration ********************/

P_TypeInfoDelegateDeclaration::P_TypeInfoDelegateDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}

/***************************** P_TypeInfoTupleDeclaration **********************/

P_TypeInfoTupleDeclaration::P_TypeInfoTupleDeclaration(Type *tinfo)
    : P_TypeInfoDeclaration(tinfo, 0)
{
}

/********************************* P_ThisDeclaration ****************************/

// For the "this" parameter to member functions

P_ThisDeclaration::P_ThisDeclaration(Type *t)
   : P_VarDeclaration(0, t, Id::This, NULL)
{
    noauto = 1;
}

P_Dsymbol *P_ThisDeclaration::syntaxCopy(P_Dsymbol *s)
{
    assert(0);		// should never be produced by syntax
    return NULL;
}

